import { ConfigService } from '@nestjs/config';
import {
  SubscribeMessage,
  WebSocketGateway,
  OnGatewayConnection,
  WebSocketServer,
  OnGatewayInit,
  OnGatewayDisconnect,
  ConnectedSocket,
  MessageBody,
} from '@nestjs/websockets';
import { Logger } from '@nestjs/common';
import { Server, Socket } from 'socket.io';
import { MongoService } from 'src/mongo/mongo.service';
import { RoomService } from 'src/room/room.service';
import { User } from 'src/decorators/nest-decorators';
@WebSocketGateway({ cors: { origin: '*' } })
export class MessageGateway
  implements OnGatewayConnection, OnGatewayInit, OnGatewayDisconnect
{
  constructor(
    private mongo: MongoService,
    private roomService: RoomService,
    private configService: ConfigService,
    private logger: Logger,
  ) {}
  @WebSocketServer()
  socket: Server;
  afterInit(server: Server) {
    server.use(async (socket, next) => {
      const room_id = socket.handshake.query.room_id as string;
      const data = {
        room_id: socket.handshake.query.room_id as string,
        random: socket.handshake.query.random as string,
        user_name: socket.handshake.query.user_name as string,
        socket_id: socket.id,
      };
      this.logger.log(
        `${data.user_name}-正在加入房间:${(await this.mongo.getRoomInfo(room_id)).room_name}`,
        MessageGateway.name,
      );
      try {
        await this.mongo.roomExist({ _id: room_id });
        const { res, exist } = await this.mongo.joinRoom(data);
        if (exist) {
          const client = server.sockets.sockets.get(res.socket_id);
          if (client) {
            client.emit('kicked', { message: '已在其他设备登陆' });
            this.logger.log(
              `${data.user_name}-踢出相同设备:${room_id}`,
              MessageGateway.name,
            );
            client.disconnect(true);
          }
        }
        this.logger.log(
          `${data.user_name}-已加入房间:${(await this.mongo.getRoomInfo(room_id)).room_name}`,
          MessageGateway.name,
        );
        next();
      } catch {
        this.logger.log(
          `${data.user_name}-加入的房间不存在:${room_id}`,
          MessageGateway.name,
        );
        next(new Error('房间不存在'));
      }
    });
  }
  async handleDisconnect(client: Socket) {
    const room_id = client.handshake.query.room_id as string;
    const user = {
      user_name: client.handshake.query.user_name as string,
      random: client.handshake.query.random as string,
    };
    this.logger.log(
      `${user.user_name}-退出房间:${(await this.mongo.getRoomInfo(room_id)).room_name}`,
      MessageGateway.name,
    );

    this.handleExitRoom(client, room_id, user);
    return this.roomService.exitRoom(room_id, user);
  }

  async handleConnection(client: Socket) {
    const room_id = client.handshake.query.room_id as string;
    const random = client.handshake.query.random as string;
    const user_name = client.handshake.query.user_name as string;
    client.join(room_id);
    this.handleRoomUpdate(client, room_id);
    this.handleJoinRoom(client, room_id, { random, user_name });
  }
  async handleRoomUpdate(client: Socket, room_id: string) {
    const users = await this.roomService.getRoomUser(room_id);
    client.to(room_id).emit('room_update', users);
  }
  async handleExitRoom(
    client: Socket,
    room_id: string,
    user: { user_name: string; random: string },
  ) {
    client.to(room_id).emit('exit_room', user);
  }
  async handleJoinRoom(
    client: Socket,
    room_id: string,
    user: { user_name: string; random: string },
  ) {
    client.to(room_id).emit('join_room', user);
  }
  @SubscribeMessage('switch_music')
  async switchMusic(client: Socket, payload: any) {
    const user = {
      random: client.handshake.query.random as string,
      user_name: client.handshake.query.user_name as string,
      room_id: client.handshake.query.room_id as string,
    };
    const music = await this.mongo.getMusic(payload.md5);
    await this.mongo.addPayList(user.room_id, {
      ...music.toJSON(),
      create_name: user.user_name,
      create_random: user.random,
    });
    await this.handlePayList(client);
    this.logger.log(
      `${(await this.mongo.getRoomInfo(user.room_id)).room_name}-${user.user_name}-播放音乐:${music.name}`,
      MessageGateway.name,
    );
    this.socket.to(user.room_id).emit('switch_music', { user, music });
  }
  @SubscribeMessage('get_paylist')
  async handlePayList(
    @ConnectedSocket() client: Socket,
    @User('room_id') room_id?: string,
  ) {
    const paylist = await this.getPaylist(room_id);
    client.to(room_id).emit('update_paylist', paylist);
  }
  async getPaylist(room_id: string) {
    return await this.mongo.getPayList(room_id);
  }
  @SubscribeMessage('playable')
  /**
   * 客户端有用户加载到可以播放的进度时调用
   */
  async handleCanPlay(
    @User('random') random: string,
    @User('room_id') room_id: string,
    @User('user_name') user_name: string,
  ) {
    await this.mongo.setStatus({ random, room_id, user_name }, 'play');
    const userCount = (await this.mongo.getUsers(room_id)).clients.length;
    const canPlayUser = await this.mongo.getCanplayUser(room_id);
    if (userCount - 1 === canPlayUser) {
      this.socket.to(room_id).emit('play');
    }
  }

  @SubscribeMessage('pause')
  /**
   * 客户端有用户暂停时
   */
  async handlePause(
    @ConnectedSocket() client: Socket,
    @User('random') random?: string,
    @User('room_id') room_id?: string,
    @User('user_name') user_name?: string,
  ) {
    this.logger.log(
      `${(await this.mongo.getRoomInfo(room_id)).room_name}-${user_name}-暂停了音乐`,
      MessageGateway.name,
    );
    this.socket.to(room_id).emit('pause');
    return this.mongo.setAllStatus(room_id, 'pause');
  }

  @SubscribeMessage('ended')
  async handleEnded(
    @ConnectedSocket() client: Socket,
    @User('user_name') user_name: string,
    @User('random') random: string,
    @User('room_id') room_id: string,
  ) {
    await this.mongo.setStatus({ user_name, random, room_id }, 'ended');
    const allUser = await this.mongo.getUsers(room_id);
    const endedNum = await this.mongo.getAllStatus(room_id, 'ended');
    if (allUser.clients.length - 1 === endedNum.length) {
      await this.mongo.delCurrent(room_id);
      const music = (await this.mongo.getPayList(room_id)).paylist;
      if (music[0]) {
        this.logger.log(
          `${(await this.mongo.getRoomInfo(room_id)).room_name}-播放完毕,自动下一首${music[0].name}`,
          MessageGateway.name,
        );
        this.switchMusic(client, { md5: music[0].md5 });
      } else {
        this.logger.log(
          `${(await this.mongo.getRoomInfo(room_id)).room_name}-列表播放完毕`,
          MessageGateway.name,
        );
        client.to(room_id).emit('pay_list_end');
      }
    }
  }
  @SubscribeMessage('play')
  async handlePlay(
    @User('random') random: string,
    @User('room_id') room_id: string,
    @User('user_name') user_name: string,
  ) {
    this.logger.log(
      `${(await this.mongo.getRoomInfo(room_id)).room_name}-${user_name}-继续播放音乐`,
      MessageGateway.name,
    );
    this.mongo.setAllStatus(room_id, 'play');
    this.socket.to(room_id).emit('play');
  }
  @SubscribeMessage('set_time')
  /**
   * 有用户调整进度
   */
  async setTime(
    @User('random') random: number,
    @User('room_id') room_id: string,
    @User('user_name') user_name: string,
    @ConnectedSocket() client: Socket,
    @MessageBody() time: number,
  ) {
    await this.handlePause(client);
    await this.mongo.setAllStatus(room_id, 'load');
    this.socket.to(room_id).emit('set_time', time);
    // this.mongo.setAllStatus()
  }
  @SubscribeMessage('add_play_list')
  /**
   * 添加播放列表
   */
  async addPlayList(
    @ConnectedSocket() socket: Socket,
    @User('random') random: string,
    @User('room_id') room_id: string,
    @User('user_name') user_name: string,
    @MessageBody() { md5 },
  ) {
    try {
      const music = await this.mongo.getMusic(md5);
      const { paylist } = await this.mongo.getPayList(room_id);
      if (paylist[0].md5 === md5) {
      } else {
        await this.mongo.addPayList(room_id, {
          ...music.toJSON(),
          create_name: user_name,
          create_random: random,
        });
        this.logger.log(
          `${(await this.mongo.getRoomInfo(room_id)).room_name}-${user_name}-${music.name}-添加到播放列表`,
          MessageGateway.name,
        );
      }
    } catch (error) {
      throw error;
    }
  }
}
